/*
* @(#)WeixinUtil.java 1.0 Jun 4, 2013
*
* Copyright @2011-2011 Gopay Information and Technology Co., Ltd.
* Sanyunqiao, Chaoyang, Beijing, 100027, China.
* All Rights Reserved.
*
* This software is the confidential and proprietary information of
* Gopay Information and technology Co., Ltd. ("Confidential Information").
* You shall not disclose such Confidential Information and shall use it only in
* accordance with the terms of the license agreement you entered into with Gopay.
*/
package com.neo.debade.weixin.util;

import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 微信公共账号工具.
 * @author zhong_xin
 * @version 1.0 Jun 4, 2013
 */
public class WeixinUtil {
	
	// 消息类型
	public static enum MsgType {
		text, 	// 文本
		image,	// 图片
		location,	// 地理
		link,	// 链接
		event,	// 事件
		
		// 返回类型
		news, // 图文
		music // 音乐
	}

	/** 接口配置认证. */
	public static String auth(HttpServletRequest request, 
			HttpServletResponse response, String token) {
		
		try {
			String signature = request.getParameter("signature");
			String timestamp = request.getParameter("timestamp");
			String nonce = request.getParameter("nonce");
			
			String s = sign(token, timestamp, nonce);
			if (s.equals(signature)) {
				System.out.println("微信验证成功");
				return request.getParameter("echostr"); 
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return "error"; 
	}
	
	
	/**
	 * 
	 * 读取请求XML.
	 * @param request
	 * @return
	 * @by zhong_xin - Dec 7, 2012 12:52:41 PM
	 */
	public static Map<String, String> readRequest(HttpServletRequest request) {
		Map<String, String> message = new HashMap<String, String>();
		try {
			ServletInputStream is = request.getInputStream();
			byte[] bytes = new byte[request.getContentLength()];
			is.read(bytes);
			String xml = new String(bytes, "UTF-8");
			System.out.println(xml);
			message = parseXml(xml);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return message;
	}
	
	/**
	 * 构建响应XML.
	 * @param map
	 * @return
	 * @by zhong_xin - Jun 4, 2013 2:37:48 PM
	 */
	public static String buildResponse(Map<String, Object> map) {
		String msgType = (String) map.get("MsgType");
		if (MsgType.text.equals(MsgType.valueOf(msgType))) {
			return String.format(RET_TEXT_TPL, 
					map.get("ToUserName"), 
					map.get("FromUserName"), 
					System.currentTimeMillis() + "",
					msgType, map.get("Content"));
		} else if (MsgType.news.equals(MsgType.valueOf(msgType))) {
			List<Map<String, Object>> articleList = (List<Map<String, Object>>) map.get("articles");
			StringBuffer articles = new StringBuffer();
			for (Map<String, Object> item : articleList) {
				articles.append(String.format(NEWS_ITEM_TPL, 
						item.get("Title"),
						item.get("Description"),
						item.get("PicUrl"),
						item.get("Url")));
			}
			
			return String.format(RET_NEWS_TPL, 
					map.get("ToUserName"), 
					map.get("FromUserName"), 
					System.currentTimeMillis() + "",
					msgType, 
					articleList.size(), 
					articles.toString());
		} else if (MsgType.music.equals(MsgType.valueOf(msgType))) {
			return String.format(RET_MUSIC_TPL, 
					map.get("ToUserName"), 
					map.get("FromUserName"), 
					System.currentTimeMillis() + "",
					msgType, 
					map.get("Title"),
					map.get("Description"),
					map.get("MusicUrl"),
					map.get("HQMusicUrl"));
		}
		return "";
	}

	private static String sign(String token, String timestamp, String nonce) {
		
		List<String> l = new ArrayList<String>();
		l.add(token);
		l.add(timestamp);
		l.add(nonce);
		Collections.sort(l);
		
		String digest = l.get(0) + l.get(1) + l.get(2);
		return sha1(digest);
	}
	
	private static String sha1(String src) {
		try {
			final int i4 = 4;
			final int i0xf = 0xf;
			char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
					'a', 'b', 'c', 'd', 'e', 'f' };
	
				byte[] strTemp = src.getBytes("UTF-8");
				MessageDigest mdTemp = MessageDigest.getInstance("SHA1");
			
			mdTemp.update(strTemp);
			byte[] md = mdTemp.digest();
			/* 转换为16进制 */
			int j = md.length;
			char[] str = new char[j * 2];
			int k = 0;
			for (int i = 0; i < j; i++) {
				byte byte0 = md[i];
				str[k++] = hexDigits[byte0 >>> i4 & i0xf];
				str[k++] = hexDigits[byte0 & i0xf];
			}
			return new String(str);
			
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	static Pattern pat = Pattern.compile("<(\\w+)>([<!\\[\\]>\\w\u4e00-\u9fa5]+)</\\w+>");
	
	private static Map<String, String> parseXml(String xml) {
		
		Map<String, String> map = new HashMap<String, String>();
		Matcher mat = pat.matcher(xml.replace("<xml>", "").replace("</xml>", ""));
		while (mat.find()) {
			String tagName = mat.group(1);
			String value = mat.group(2).trim();
//			System.out.println(tagName + " = " + value);
			if (value.startsWith("<![CDATA[")) {
				value = value.substring("<![CDATA[".length(), value.length() - 3);
			}
			map.put(tagName.trim(), value.trim());
		}
		return map;
	}
	
	// 返回文本信息模板
	static String RET_TEXT_TPL = "<xml>" +
			"<ToUserName><![CDATA[%s]]></ToUserName>" +
			"<FromUserName><![CDATA[%s]]></FromUserName>" +
			"<CreateTime>%s</CreateTime>" +
			"<MsgType><![CDATA[%s]]></MsgType>" +
			"<Content><![CDATA[%s]]></Content>" +
			"<FuncFlag>0</FuncFlag>" +
			"</xml>";
	
	// 返回图文信息模板
	static String RET_NEWS_TPL = "<xml>" + 
			"<ToUserName><![CDATA[%s]]></ToUserName>" + 
			"<FromUserName><![CDATA[%s]]></FromUserName>" + 
			"<CreateTime>%s</CreateTime>" + 
			"<MsgType><![CDATA[%s]]></MsgType>" + 
			"<ArticleCount>%s</ArticleCount>" + 
			"<Articles>%s</Articles>" + 
			"<FuncFlag>1</FuncFlag>" + 
			"</xml>";
	
	// 音乐信息模板
	static String RET_MUSIC_TPL = "<xml>" +
			"<ToUserName><![CDATA[%s]]></ToUserName>" +
			"<FromUserName><![CDATA[%s]]></FromUserName>" +
			"<CreateTime>%s</CreateTime>" +
			"<MsgType><![CDATA[%s]]></MsgType>" +
			"<Music>" +
			"<Title><![CDATA[%s]]></Title>" +
			"<Description><![CDATA[%s]]></Description>" +
			"<MusicUrl><![CDATA[%s]]></MusicUrl>" +
			"<HQMusicUrl><![CDATA[%s]]></HQMusicUrl>" +
			"</Music>" +
			"<FuncFlag>0</FuncFlag>" +
			"</xml>";
	
	// 图文信息文章模板
	static String NEWS_ITEM_TPL = "<item>" + 
			"<Title><![CDATA[%s]]></Title> " + 
			"<Description><![CDATA[%s]]></Description>" + 
			"<PicUrl><![CDATA[%s]]></PicUrl>" + 
			"<Url><![CDATA[%s]]></Url>" + 
			"</item>";
}
